//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include "dbgstub.h"
#include <_schedule.h>

#if defined(_arm) || defined(_x86)

ProcessId g_compoundChannelWriteHolderPID;
ThreadId g_compoundChannelWriteHolderTID;
DzEvent g_compoundChannelWriteFinsihedEvent;

Address g_Dbgstub_ThreadEntry = NULL;
Address g_Dbgstub_DebugBreakThread = NULL;

CARAPI DebuggerStub::Read(
        /* [in] */ Int64 u64Offset,
        /* [in] */ Int32 bytesToRead,
        /* [out] */ MemoryBuf * pBuffer,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_ACCESS_DENIED;
}

CARAPI DebuggerStub::Write(
        /* [in] */ Int64 u64Offset,
        /* [in] */ const MemoryBuf & buffer,
        /* [out] */ Int32 * pBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_ACCESS_DENIED;
}

CARAPI DebuggerStub::Control(
        /* [in] */ Handle32 nControlCode,
        /* [in] */ const MemoryBuf & inBuffer,
        /* [out] */ MemoryBuf * pOutBuffer,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    if ((nControlCode != DBGSTUB_CTRL_CODE_DETACH_PROCESS)
            && inBuffer.IsNullOrEmpty()) {
        return E_INVALID_ARGUMENT;
    }

    ECode ec;

    UInt32 * pInArg = (UInt32 *)inBuffer.GetPayload();
    UInt32 nInArgSize = inBuffer.GetUsed();  // the number of bytes

    switch (nControlCode) {
        case DBGSTUB_CTRL_CODE_ATTACH_PROCESS:
        {
            if (nInArgSize < (Int32)sizeof(Int32)) {
                return E_INVALID_ARGUMENT;
            }
            return AttachActiveProcess(
                    *pInArg);       // ID for the process to be debugged
        }

        case DBGSTUB_CTRL_CODE_DETACH_PROCESS:
        {
            return DetachActiveProcess();
        }

        case DBGSTUB_CTRL_CODE_WAIT_FOR_DEBUGEVENT:
        {
            if (pOutBuffer->IsNull()
                    || pOutBuffer->GetCapacity() < (Int32)sizeof(DEBUG_EVENT)
                    || nInArgSize < (Int32)sizeof(UInt32)) {
                return E_INVALID_ARGUMENT;
            }
            ec = WaitForDebugEvent(
                        (LPDEBUG_EVENT)pOutBuffer->GetPayload(),  // DEBUG_EVENT
                        *(UInt32*)pInArg);     // Number of milliseconds to wait
            if (SUCCEEDED(ec)) {
                pOutBuffer->SetUsed(sizeof(DEBUG_EVENT));
            }
            return ec;
        }

        case DBGSTUB_CTRL_CODE_CONTINUE:
        {
            if (nInArgSize < (Int32)(sizeof(UInt32) * 2)) {
                return E_INVALID_ARGUMENT;
            }
            return ContinueDebugEvent(
                        pInArg[0],      // Thread ID
                        pInArg[1]);     // continuation status
        }

        case DBGSTUB_CTRL_CODE_SET_THREAD_CONTEXT:
        {
            if (nInArgSize < (Int32)(sizeof(Context) + sizeof(UInt32))) {
                return E_INVALID_ARGUMENT;
            }
            return SetThreadContext(
                        *pInArg,                    // Thread ID
                        (Context *)(pInArg + 1));   // Pointer to the Context
        }

        case DBGSTUB_CTRL_CODE_GET_THREAD_CONTEXT:
        {
            if (nInArgSize < (Int32)sizeof(UInt32) || pOutBuffer->IsNull()
                    || pOutBuffer->GetCapacity()  < (Int32)sizeof(Context)) {
                return E_INVALID_ARGUMENT;
            }
            ec = GetThreadContext(
                    *pInArg,                           // Thread ID
                    (Context*)pOutBuffer->GetPayload()); // Pointer to the Context
            if (SUCCEEDED(ec)) {
                pOutBuffer->SetUsed(sizeof(Context));
            }
            return ec;
        }

        case DBGSTUB_CTRL_CODE_READ_PROCESS_MEMORY:
        {
            if (nInArgSize < (Int32)sizeof(Int32) * 2) {
                return E_INVALID_ARGUMENT;
            }

            PVoid base = (PVoid)pInArg[0];
            size_t size = pInArg[1];

            if (pOutBuffer->IsNull() || pOutBuffer->GetCapacity() < (Int32)size) {
                return E_INVALID_ARGUMENT;
            }

            PVoid pBuf = (PVoid)pOutBuffer->GetPayload();
            size_t uRead;

            ec = ReadProcessMemory(base, pBuf, size, &uRead);
            if (SUCCEEDED(ec)) {
                pOutBuffer->SetUsed(uRead);
            }
            return ec;
        }

        case DBGSTUB_CTRL_CODE_WRITE_PROCESS_MEMORY:
        {
            if (nInArgSize < (Int32)(2 * sizeof(Int32))
                    || pOutBuffer->IsNull()
                    || pOutBuffer->GetCapacity() < (Int32)sizeof(Int32)) {
                return E_INVALID_ARGUMENT;
            }

            PVoid base = (PVoid)pInArg[0];
            size_t size = pInArg[1];
            PVoid pBuf = pInArg + 2;

            if (nInArgSize < size + 8) {
                return E_INVALID_ARGUMENT;
            }
            ec = WriteProcessMemory(base, pBuf, size,
                    (size_t *)(PVoid)pOutBuffer->GetPayload());
            if (SUCCEEDED(ec)) {
                pOutBuffer->SetUsed(sizeof(Int32));
            }
            return ec;
        }

        case DBGSTUB_CTRL_CODE_SET_BREAKPIONT:
        {
            if (nInArgSize < (Int32)sizeof(Int32)
                    || pOutBuffer->IsNull()
                    || pOutBuffer->GetCapacity() < DBG_INS_LENGTH) {
                return E_INVALID_ARGUMENT;
            }

            ec = SetBreakPoint(
                    *(UInt32 *)inBuffer.GetPayload(),// thread ID
                    (PVoid)pOutBuffer->GetPayload());  // the original instruction
            if (SUCCEEDED(ec)) {
                pOutBuffer->SetUsed(DBG_INS_LENGTH);
            }
            return ec;
        }

        case DBGSTUB_CTRL_CODE_GET_THREAD_STACK_BASE:
        {
            if (nInArgSize < (Int32)sizeof(Int32)
                    || pOutBuffer->IsNull()
                    || pOutBuffer->GetCapacity() < (Int32)sizeof(PVoid)) {
                return E_INVALID_ARGUMENT;
            }

            ec = GetThreadStackBase(
                    *(UInt32 *)(pInArg + 1),             // thread ID
                    (PVoid *)pOutBuffer->GetPayload());   // PVoid * pStackBase
            if (SUCCEEDED(ec)) {
                pOutBuffer->SetUsed((Int32)sizeof(PVoid));
            }
            return ec;
        }

        case DBGSTUB_CTRL_CODE_SUSPEND_PROCESS:
        {
            if (nInArgSize != (Int32)(sizeof(Int32))
                    || pOutBuffer->IsNull()
                    || pOutBuffer->GetCapacity() < (Int32)sizeof(PVoid)) {
                return E_INVALID_ARGUMENT;
            }

            ProcessId dwProcessId = (ProcessId)pInArg[0];
            if (NULL == g_Dbgstub_ThreadEntry
                    || NULL == g_Dbgstub_DebugBreakThread) {
                assert(0);
                return E_INVALID_ARGUMENT;
            }

            Address entry = g_Dbgstub_ThreadEntry;
            Address UserEntry = g_Dbgstub_DebugBreakThread;

            UInt32 nUserArg;
            ThreadId *id = (ThreadId *)pOutBuffer->GetPayload();
            ec = SuspendProcess(dwProcessId, entry, UserEntry, &nUserArg, id);
            if (SUCCEEDED(ec)) {
                pOutBuffer->SetUsed((Int32)sizeof(ThreadId));
            }

            return ec;
        }
        default:
            break;
    }

    assert(0);
    return E_INVALID_ARGUMENT;
}

CProcess * DebuggerStub::GetProcessById(ProcessId pid)
{
    CProcess *pProc;
    ForEachDLinkNode(CProcess *, pProc, &(CProcess::s_processList)) {
        if (GetProcessId(pProc) == pid)
            return pProc;
    }
    return NULL;
}

Thread * DebuggerStub::GetThreadFromProcess(CProcess * pProcess, ThreadId tid)
{
    assert(pProcess);

    Thread *pThd;
    InProcessLinkNode * pInPro;
    ForEachDLinkNode(InProcessLinkNode*, pInPro, &(pProcess->m_threadList)) {
        pThd = THIS(pInPro, Thread, m_inProcLink);
        if (tid == GetThreadId(pThd)) {
            return pThd;
        }
    }
    return NULL;
}

Thread * DebuggerStub::GetThreadFromTargetProcess(ThreadId tid)
{
    assert(m_pTargetProcess);

    return GetThreadFromProcess(m_pTargetProcess, tid);
}

ECode DebuggerStub::WaitForDebugEvent(
        /* [out] */ LPDEBUG_EVENT pDebugEvent,      // debug event information
        /* [in] */ UInt32 dwMilliseconds             // time-out value
)
{
    if (GetThreadId() != m_nDebuggerThreadId) {
        return E_DBGSTUB_ACCESS_DENIED;
    }

    Boolean bPreemption = DzDisablePreemption();

    if (m_pTargetProcess == NULL
        || m_pTargetProcess->m_DebugHandle == PROCESS_SIGNATURE) {
        DzRestorePreemption(bPreemption);
        return E_DBGSTUB_PROCESS_NOT_EXIST;
    }

    DzRestorePreemption(bPreemption);

    WaitResult result;
    EventState state;

    m_WaitEvent.TryWait(DzMillisecondsToTicks(dwMilliseconds), &result, &state);
    if (result == WaitResult_TimedOut) {
        return E_TIMED_OUT;
    }

    assert(result == WaitResult_OK);

    memcpy(pDebugEvent, &m_DebugEvent, sizeof(DEBUG_EVENT));

    return NOERROR;
}

ECode DebuggerStub::ContinueDebugEvent(
        /* [in] */ UInt32 dwThreadId,        // thread to continue
        /* [in] */ UInt32 dwContinueStatus   // continuation status
)
{
    if (dwThreadId == 0) {
        dwThreadId = m_nTriggerThreadId;
    }
    if (dwThreadId != (DWORD)m_nTriggerThreadId) {
        return E_DBGSTUB_PROCESS_NOT_EXIST;
    }

    ECode ec = CheckIntegrality();
    if (FAILED(ec)) {
        return ec;
    }

    if (!m_nTriggerThreadId) {
        return E_DBGSTUB_WAITER_NOT_EXIST;
    }

    m_WaitEvent.Clear();
    m_ContinueEvent.Notify(dwContinueStatus);

    return NOERROR;
}

void DebuggerStub::FillDebugEvent(EventState state, UInt32 handle)
{
    switch (state) {
        case DEBUGGEE_EXCEPTION :
        {
            m_DebugEvent.dwDebugEventCode = DEBUGGEE_EXCEPTION;
            m_DebugEvent.dwThreadId = m_nTriggerThreadId;
            m_DebugEvent.u.Exception.dwFirstChance = 0;
            PEXCEPTION_RECORD pExcInfo =
                &(m_DebugEvent.u.Exception.ExceptionRecord);
            UInt32 *pHandle = (UInt32 *)handle;
            pExcInfo->ExceptionCode = pHandle[0];
            pExcInfo->ExceptionAddress = (PVoid)pHandle[1];

            break;
        }

        case DEBUGGEE_LOAD_DLL :
        case DEBUGGEE_UNLOAD_DLL :
        {
            m_DebugEvent.dwDebugEventCode = state;
            m_DebugEvent.dwThreadId = m_nTriggerThreadId;

            CModule *pModule = (CModule *)handle;
            LPLOAD_DLL_DEBUG_INFO pDllInfo = (LPLOAD_DLL_DEBUG_INFO)&(m_DebugEvent.u);
            pDllInfo->lpBaseOfDll = (PVoid)pModule->m_uImageBase;
            wcsncpy(pDllInfo->lpImageName, pModule->m_wszFullPath, 80);

            break;
        }

        case DEBUGGEE_THREAD_CREATE:
        {
            m_DebugEvent.dwDebugEventCode = DEBUGGEE_THREAD_CREATE;
            m_DebugEvent.dwThreadId = m_nTriggerThreadId;
            UInt32 *arg = (UInt32*)handle;
            Thread * pThd = (Thread*)arg[0];
            CREATE_THREAD_DEBUG_INFO *pCreateThreadInfo
                = (CREATE_THREAD_DEBUG_INFO*)&(m_DebugEvent.u);
            pCreateThreadInfo->ThreadID = GetThreadId(pThd);
            pCreateThreadInfo->lpThreadLocalBase = pThd->m_uUserStackBase
                + USER_STACK_SIZE - USER_STACK_PRESERVED_SIZE;
            pCreateThreadInfo->lpStartAddress = arg[1];

            break;
        }

        case DEBUGGEE_THREAD_EXIT :
        {
            m_DebugEvent.dwDebugEventCode = DEBUGGEE_THREAD_EXIT;
            m_DebugEvent.dwThreadId = m_nTriggerThreadId;
            EXIT_THREAD_DEBUG_INFO *pExitThreadInfo
                = (EXIT_THREAD_DEBUG_INFO*)&(m_DebugEvent.u);
            pExitThreadInfo->dwExitCode = handle;

            break;
        }

        case DEBUGGEE_PROCESS_CREATE:
        {
            m_DebugEvent.dwDebugEventCode = DEBUGGEE_PROCESS_CREATE;
            m_DebugEvent.dwThreadId = m_nTriggerThreadId;
            CProcess *pPrc = (CProcess*)handle;
            assert(pPrc == m_pTargetProcess);
            CREATE_PROCESS_DEBUG_INFO *pPrcInfo
                = (CREATE_PROCESS_DEBUG_INFO*)&(m_DebugEvent.u);

            pPrcInfo->ThreadID = GetThreadId(pPrc->m_pMainThread);
            pPrcInfo->lpBaseOfImage
                = pPrc->m_pMainModule->m_pModule->m_uImageBase;
            pPrcInfo->lpStartAddress =
                pPrc->m_pMainModule->m_pModule->m_uEntryPoint;
            pPrcInfo->lpThreadLocalBase
                = pPrc->m_pMainThread->m_uUserStackBase
                + USER_STACK_SIZE - USER_STACK_PRESERVED_SIZE;

            break;
        }

        case DEBUGGEE_PROCESS_EXIT :
        {
            m_DebugEvent.dwDebugEventCode = DEBUGGEE_PROCESS_EXIT;
            m_DebugEvent.dwThreadId = m_nTriggerThreadId;
            CProcess *pPrc = (CProcess*)handle;
            assert(pPrc == m_pTargetProcess);
            EXIT_PROCESS_DEBUG_INFO *pPrcInfo
                = (EXIT_PROCESS_DEBUG_INFO*)&(m_DebugEvent.u);

            pPrcInfo->dwExitCode = pPrc->m_nExitCode;

            break;
        }

        case DEBUGGEE_PROCESS_STOP :
            assert(m_bHaveStopped);
            m_DebugEvent.dwDebugEventCode = DEBUGGEE_PROCESS_STOP;
            m_DebugEvent.dwThreadId = m_nTriggerThreadId;

            break;

        default:
            assert0();
    }
}

void DebuggerStub::SendDebugEvent(
    CProcess * pTargetProcess,
    Thread * pTargetThread,
    EventState state,
    UInt32 handle)
{
    assert(NULL != pTargetProcess);
    assert(NULL != pTargetThread);

    WaitResult result;

    Boolean bAllThreadSuspended = TRUE;

    Boolean bPreemption = FALSE;
    do {
        // Suspend all target threads and send the debug message
        //
        pTargetProcess->m_threadLock.Lock();
        bPreemption = DzDisablePreemption();

        if (pTargetProcess->m_DebugHandle != (UInt32)this) {
            DzRestorePreemption(bPreemption);
            pTargetProcess->m_threadLock.Unlock();
            return;
        }

        assert(pTargetProcess == m_pTargetProcess);

        pTargetThread->GetId(&m_nTriggerThreadId);

        bAllThreadSuspended = SuspendAllTargetThreads(pTargetProcess);

        FillDebugEvent(state, handle);

        DzRestorePreemption(bPreemption);
        pTargetProcess->m_threadLock.Unlock();

        // if there is a thread holding compound channel object, wait it to
        // finish its compound channel session. and then suspend it
        if (!bAllThreadSuspended) {
            g_compoundChannelWriteFinsihedEvent.Wait(&result, NULL);
        }
    } while (!bAllThreadSuspended);

    // Notify the debugger
    //
    m_WaitEvent.Notify(state);

    if (::GetCurrentProcess() == pTargetProcess) {
        m_ContinueEvent.Wait(&result, NULL);
    }

    // Resume all target threads
    //
    pTargetProcess->m_threadLock.Lock();
    bPreemption = DzDisablePreemption();

    ResumeAllTargetThreads(pTargetProcess);
    m_nTriggerThreadId = 0;

    DzRestorePreemption(bPreemption);
    pTargetProcess->m_threadLock.Unlock();
}

Boolean DebuggerStub::SuspendAllTargetThreads(CProcess * pTargetProcess)
{
    assert(!PreemptionEnabled());
    assert(NULL != pTargetProcess);

    Boolean bProcessHoldingChannel = GetProcessId(pTargetProcess) == g_compoundChannelWriteHolderPID;
    Boolean bAllSuspended = TRUE;
    Thread * pCurrentThread = ::GetCurrentThread();

    InProcessLinkNode * pInPro;
    ForEachDLinkNode(InProcessLinkNode*, pInPro, &(pTargetProcess->m_threadList)) {
        Thread * pThd = THIS(pInPro, Thread, m_inProcLink);
        if (pCurrentThread != pThd) {
            if (bProcessHoldingChannel && GetThreadId(pThd) == g_compoundChannelWriteHolderTID) {
                bAllSuspended = FALSE;
                continue; // skip this thread
            }
            uint32_t uFlags = SaveFlagsAndCli();

            if (pThd->IsRunning())
                pThd->m_pEffectiveScheduleClass->LeaveRunningList(pThd);

            pThd->m_uState |= ThreadState_DebugSuspended;

            RestoreIF(uFlags);
        }
    }
    return bAllSuspended;
}

void DebuggerStub::ResumeAllTargetThreads(CProcess * pTargetProcess)
{
    assert(!PreemptionEnabled());
    assert(NULL != pTargetProcess);

    Thread * pCurrentThread = ::GetCurrentThread();

    InProcessLinkNode * pInPro;
    ForEachDLinkNode(InProcessLinkNode*, pInPro, &(pTargetProcess->m_threadList)) {
        Thread * pThd = THIS(pInPro, Thread, m_inProcLink);
        if (pCurrentThread != pThd) {
            uint32_t uFlags = SaveFlagsAndCli();

            pThd->m_uState &= ~ThreadState_DebugSuspended;
            if (pThd->IsRunning())
                pThd->m_pEffectiveScheduleClass->EnterRunningList(pThd,
                    RunningReason_Resume);

            RestoreIF(uFlags);
        }
    }
}

ECode DebuggerStub::CheckIntegrality()
{
    ECode ec = NOERROR;

    if (GetThreadId() != m_nDebuggerThreadId) {
        return E_DBGSTUB_ACCESS_DENIED;
    }

    CProcess::s_processListLock.Lock();

    if (m_pTargetProcess == NULL
        || GetProcessById(m_nTargetProcessId) == NULL
        || m_pTargetProcess->m_DebugHandle == PROCESS_SIGNATURE) {
        ec = E_DBGSTUB_PROCESS_NOT_EXIST;
    }

    CProcess::s_processListLock.Unlock();
    return ec;
}

ECode DebuggerStub::CheckIntegralityAndDisablePreemption(PBoolean pPreemption)
{
    assert(pPreemption);
    ECode ec = NOERROR;

    *pPreemption = FALSE;

    if (GetThreadId() != m_nDebuggerThreadId) {
        return E_DBGSTUB_ACCESS_DENIED;
    }

    CProcess::s_processListLock.Lock();

    if (m_pTargetProcess == NULL
        || GetProcessById(m_nTargetProcessId) == NULL
        || m_pTargetProcess->m_DebugHandle == PROCESS_SIGNATURE) {
        ec = E_DBGSTUB_PROCESS_NOT_EXIST;
        goto EXIT;
    }

    m_pTargetProcess->m_threadLock.Lock();

    *pPreemption = DzDisablePreemption();

    m_pTargetProcess->m_threadLock.Unlock();

EXIT:
    CProcess::s_processListLock.Unlock();
    return ec;
}

ECode DebuggerStub::CheckIntegralityAndLockMemoryAllocator(PBoolean pPreemption)
{
    assert(pPreemption);
    ECode ec = NOERROR;

    *pPreemption = FALSE;

    if (GetThreadId() != m_nDebuggerThreadId) {
        return E_DBGSTUB_ACCESS_DENIED;
    }

    CProcess::s_processListLock.Lock();

    if (m_pTargetProcess == NULL
        || GetProcessById(m_nTargetProcessId) == NULL
        || m_pTargetProcess->m_DebugHandle == PROCESS_SIGNATURE) {
        ec = E_DBGSTUB_PROCESS_NOT_EXIST;
        goto EXIT;
    }

    m_pTargetProcess->m_threadLock.Lock();

    *pPreemption = LockMemoryAllocatorAndDisablePreemption();

    m_pTargetProcess->m_threadLock.Unlock();

EXIT:
    CProcess::s_processListLock.Unlock();
    return ec;
}

ECode DebuggerStub::AttachActiveProcess(
        /* [in] */ UInt32 dwProcessId    // process to be debugged
)
{
    ECode ec = NOERROR;
    CProcess * pProc = NULL;

    CProcess::s_processListLock.Lock();
    Boolean bPreemption = DzDisablePreemption();

    if (m_pTargetProcess != NULL) {
        ec = E_DBGSTUB_ALREADY_USED;
        goto Exit;
    }

    pProc = GetProcessById(dwProcessId);
    if (pProc == ::GetCurrentProcess()) {
        ec = E_DBGSTUB_DEBUG_ITSELF;
        goto Exit;
    }
    else if (pProc == NULL) {
        pProc = (CProcess*)(dwProcessId | 0x80000000);
        if (pProc < krn.vaKernelVirtBase || pProc > g_vaKernelHighMemory
            || pProc->m_DebugHandle != PROCESS_SIGNATURE
            || pProc->m_processState != ProcessState_Unstarted) {
            ec = E_DBGSTUB_PROCESS_NOT_EXIST;
            goto Exit;
        }
        else {
            m_bHaveStopped = TRUE;
        }
    }

    assert(pProc);

    if (pProc->m_DebugHandle == PROCESS_SIGNATURE) {
        pProc->m_DebugHandle = (UInt32)this;
        m_pTargetProcess = pProc;
        m_pTargetProcess->AddRef();
        m_nTargetProcessId = dwProcessId;
        m_nDebuggerThreadId = GetThreadId();
    }
    else {
        ec = E_DBGSTUB_ALREADY_EXIST;
    }

Exit:
    DzRestorePreemption(bPreemption);
    CProcess::s_processListLock.Unlock();
    return ec;
}

ECode DebuggerStub::DetachActiveProcess()
{
    ECode ec = NOERROR;
    DzEvent * pContinueEvent = NULL;
    CProcess * pProcess = NULL;

    Boolean bPreemption = DzDisablePreemption();

    if (NULL == m_pTargetProcess) {
        ec = E_DBGSTUB_PROCESS_NOT_EXIST;
        goto Exit;
    }

    assert(0 != m_nDebuggerThreadId);

    if (m_pTargetProcess->m_DebugHandle == PROCESS_SIGNATURE) {
        ec = E_DBGSTUB_NOT_EXIST;
    }
    else if (0 != m_nTriggerThreadId) {
        pContinueEvent = &m_ContinueEvent;
    }

    m_nDebuggerThreadId = 0;
    m_WaitEvent.Clear();
    m_pTargetProcess->m_DebugHandle = PROCESS_SIGNATURE;
    m_pTargetProcess = NULL;
    pProcess = m_pTargetProcess;
    m_nTargetProcessId = 0;

    m_bHaveStopped = FALSE;
    m_nTriggerThreadId = 0;

Exit:
    DzRestorePreemption(bPreemption);

    if (NULL != pContinueEvent) {
        pContinueEvent->Notify(0xabadbeef);
    }

    if (NULL != pProcess) {
        pProcess->Release();
    }

    return ec;
}

ECode DebuggerStub::GetThreadContext(
        /* [in] */ UInt32 dwThreadID,        // handle to thread with context
        /* [out] */ Context *pContext       // context structure
)
{
    Boolean bPreemption;
    Thread * pThd = NULL;

    ECode ec = CheckIntegralityAndDisablePreemption(&bPreemption);
    if (SUCCEEDED(ec)) {
        if (dwThreadID == 0) {
            dwThreadID = m_nTriggerThreadId;
        }
        pThd = GetThreadFromTargetProcess(dwThreadID);
    }
    else {
        bPreemption = DzDisablePreemption();
        pThd = GetThreadFromProcess(::GetCurrentProcess(), dwThreadID);
        if (pThd == ::GetCurrentThread()) {
            pThd = NULL;
        }
    }

    if (pThd != NULL) {
#if defined(_arm)
        memcpy(pContext,(InterruptContext *)pThd->m_vaKernelStack - 1,
                sizeof(InterruptContext));
#elif defined(_x86)
        DebugContext dbgCtx;
        m_pTargetProcess->GetDebugRegisters(&dbgCtx);

        memcpy(&(pContext->m_Context),
                (InterruptContext *)pThd->m_vaKernelStack - 1,
                sizeof(InterruptContext));
        memcpy(&(pContext->m_DebugRegInfo), &dbgCtx, sizeof(DebugRegisterInfo));
#else
#error Unknown architecture
#endif
    }
    else {
        ec = E_DBGSTUB_THREAD_NOT_EXIST;
    }

    DzRestorePreemption(bPreemption);

    return ec;
}

ECode DebuggerStub::SetThreadContext(
        /* [in] */ UInt32 dwThreadID,        // handle to thread with context
        /* [in] */ Context *pContext        // context structure
)
{
    Boolean bPreemption;
    Thread * pThd = NULL;

    ECode ec = CheckIntegralityAndDisablePreemption(&bPreemption);
    if (SUCCEEDED(ec)) {
        if (dwThreadID == 0) {
            dwThreadID = m_nTriggerThreadId;
        }
        pThd = GetThreadFromTargetProcess(dwThreadID);
    }
    else {
        bPreemption = DzDisablePreemption();
        pThd = GetThreadFromProcess(::GetCurrentProcess(), dwThreadID);
        if (pThd == ::GetCurrentThread()) {
            pThd = NULL;
        }
    }

    if (pThd != NULL) {
        InterruptContext *pTargetIContext =
                (InterruptContext *)pThd->m_vaKernelStack - 1;

#if defined(_arm)
    pTargetIContext->r0 = pContext->r0;
    pTargetIContext->r1 = pContext->r1;
    pTargetIContext->r2 = pContext->r2;
    pTargetIContext->r3 = pContext->r3;
    pTargetIContext->r4 = pContext->r4;
    pTargetIContext->r5 = pContext->r5;
    pTargetIContext->r6 = pContext->r6;
    pTargetIContext->r7 = pContext->r7;
    pTargetIContext->r8 = pContext->r8;
    pTargetIContext->r9 = pContext->r9;
    pTargetIContext->r10 = pContext->r10;
    pTargetIContext->r11 = pContext->r11;
    pTargetIContext->r12 = pContext->r12;
    pTargetIContext->sp = pContext->sp;
    pTargetIContext->lr = pContext->lr;
    pTargetIContext->pc = pContext->pc;
    pTargetIContext->cpsr =
        (pTargetIContext->cpsr & 0xFF) | (pContext->cpsr & 0xFF000000);
#elif defined(_x86)
        InterruptContext *pInterContext =
                (InterruptContext *)&pContext->m_Context;
        pTargetIContext->eip = pInterContext->eip;
        pTargetIContext->eflags = pInterContext->eflags;
        pTargetIContext->_esp = pInterContext->_esp;
        pTargetIContext->ebx = pInterContext->ebx;
        pTargetIContext->ecx = pInterContext->ecx;
        pTargetIContext->edx = pInterContext->edx;
        pTargetIContext->esi = pInterContext->esi;
        pTargetIContext->edi = pInterContext->edi;
        pTargetIContext->ebp = pInterContext->ebp;
        pTargetIContext->eax = pInterContext->eax;

        m_pTargetProcess->SetDebugRegisters(
                (DebugContext *)&(pContext->m_DebugRegInfo));
#else
#error Unknown architecture
#endif
    }
    else {
        ec = E_DBGSTUB_THREAD_NOT_EXIST;
    }

    DzRestorePreemption(bPreemption);

    return ec;
}

Boolean DebuggerStub::CheckMemoryBlock(
        UInt32 base, UInt32 size, VSegment ** ppSegment)
{
    assert(!PreemptionEnabled());
    assert(size <= PAGE_SIZE);
    assert(base < base + size);

    DLinkNode *pCurrentList;
    VSegment *pSeg;

    if (ppSegment) *ppSegment= NULL;

    if (base < COMMON_LIMIT) {
        pCurrentList = &m_pTargetProcess->m_AS.m_commList;
    }
    else {
        pCurrentList = &m_pTargetProcess->m_AS.m_privList;
    }

    ForEachDLinkNode(VSegment *, pSeg, pCurrentList) {
        if (pSeg->m_uBase <= base
                && (base + size) <= (pSeg->m_uBase + pSeg->m_uSize)) {
            if (ppSegment) *ppSegment= pSeg;
            return TRUE;
        }
    }

    if ((PROXY_ENTRY_BASE <= base )
            && ((base + size) <= (PROXY_ENTRY_BASE + PAGE_SIZE))) {
        return TRUE;
    }

    if ((USER_BLOCK_BASE <= base)
            && (base + size) <= (USER_BLOCK_BASE + USER_BLOCK_SIZE)) {
        return TRUE;
    }

    return FALSE;
}

ECode DebuggerStub::ReadProcessMemory(
        /* [in] */ PVoid pBaseAddress,              // base of memory area
        /* [out] */ PVoid pBuffer,                  // data buffer
        /* [in] */ size_t nSize,                    // number of bytes to read
        /* [out] */ size_t * pNumberOfBytesRead     // number of bytes read
)
{
    assert(pBuffer);

    if (nSize > DBGSTUB_BUFFER_SIZE
        || (UInt32)pBaseAddress >= PROCESS_TOP
        || (UInt32)pBaseAddress + nSize >= PROCESS_TOP)
        return E_INVALID_ARGUMENT;

    Boolean bPreemption;
    ECode ec = CheckIntegralityAndDisablePreemption(&bPreemption);
    if (FAILED(ec)) {
        return ec;
    }

    if (CheckMemoryBlock((UInt32)pBaseAddress, nSize, NULL)) {
        CProcess * pCurPrc = GetCurrentProcess();
        assert(pCurPrc);

        m_pTargetProcess->m_AS.Activate();
        DzRestorePreemption(bPreemption);
        m_pTargetProcess->m_AS.Hit((UInt32)pBaseAddress);
        ec = CheckIntegralityAndDisablePreemption(&bPreemption);
        if (FAILED(ec)) {
            return ec;
        }
        memcpy(m_RWBuf, pBaseAddress, nSize);
        pCurPrc->m_AS.Activate();

        memcpy(pBuffer, m_RWBuf, nSize);
        if (pNumberOfBytesRead) {
            *pNumberOfBytesRead = nSize;
        }
    }
    else {
        ec = E_ACCESS_DENIED;
    }

    DzRestorePreemption(bPreemption);

    return ec;
}

ECode DebuggerStub::WriteProcessMemory(
        /* [in] */ PVoid pBaseAddress,              // base of memory area
        /* [in] */ PVoid pBuffer,                   // data buffer
        /* [in] */ size_t nSize,                    // count of bytes to write
        /* [out] */ size_t * pNumberOfBytesWritten  // count of bytes written
)
{
    Boolean bPreemption;
    ECode ec = CheckIntegralityAndLockMemoryAllocator(&bPreemption);
    if (FAILED(ec)) {
        return ec;
    }

    ec = ExchangeProcessMemory(pBaseAddress, pBuffer, nSize, NULL);
    if (SUCCEEDED(ec) && pNumberOfBytesWritten) {
        *pNumberOfBytesWritten = nSize;
    }

    UnlockMemoryAllocatorAndRestorePreemption(bPreemption);

    return ec;
}

ECode DebuggerStub::SetBreakPoint(
        /* [in] */ UInt32 dwThreadID,
        /* [out] */ PVoid pOrigInstruction)
{
    assert(pOrigInstruction);

    Boolean bPreemption;
    ECode ec = CheckIntegralityAndLockMemoryAllocator(&bPreemption);
    if (FAILED(ec)) {
        return ec;
    }

    if (dwThreadID == 0) {
        dwThreadID = m_nTriggerThreadId;
    }

    Thread * pThd = GetThreadFromTargetProcess(dwThreadID);
    if (pThd != NULL) {
        UInt32 bps = ELASTOS_GDBSERVER_BREAKPOINT;
        InterruptContext * pContext =
                (InterruptContext *)pThd->m_vaKernelStack - 1;
#if defined(_arm)
        UInt32 baseAddress = pContext->pc;
#elif defined(_x86)
        UInt32 baseAddress = pContext->eip;
#else
#error Unknown architecture
#endif
        char buf[DBG_INS_LENGTH];
        ec = ExchangeProcessMemory((PVoid)baseAddress, (PVoid)&bps,
                        DBG_INS_LENGTH, (PVoid)buf);
        if (SUCCEEDED(ec)) {
            memcpy(pOrigInstruction, buf, DBG_INS_LENGTH);
        }
    }
    else {
        ec = E_DBGSTUB_THREAD_NOT_EXIST;
    }

    UnlockMemoryAllocatorAndRestorePreemption(bPreemption);

    return ec;
}

ECode DebuggerStub::ExchangeProcessMemory(
        /* [in] */ PVoid pBaseAddress,  // base of memory area
        /* [in] */ PVoid pBuffer,       // data buffer
        /* [in] */ size_t nSize,        // count of bytes to be written
        /* [out] */ PVoid pOutBuffer    // buffer that receives data
)
{
    assert(!PreemptionEnabled());

    ECode ec = NOERROR;
    VSegment * pSegment = NULL;

    if (nSize > DBGSTUB_BUFFER_SIZE
        || (UInt32)pBaseAddress >= PROCESS_TOP
        || (UInt32)pBaseAddress + nSize >= PROCESS_TOP) {
        return E_INVALID_ARGUMENT;
    }

    memcpy(m_RWBuf, pBuffer, nSize);

    CProcess * pCurPrc = GetCurrentProcess();
    assert(pCurPrc);

    if (!CheckMemoryBlock((UInt32)pBaseAddress, nSize, &pSegment)) {
        return E_ACCESS_DENIED;
    }

    m_pTargetProcess->m_AS.Activate();

    if (pSegment) {
        ec = pSegment->CloneMemBlockForProcess((UInt32)pBaseAddress, nSize);
    }

    if (SUCCEEDED(ec)) {
        if (pOutBuffer) {
            memcpy(pOutBuffer, pBaseAddress, nSize);
        }
        memcpy(pBaseAddress, m_RWBuf, nSize);
    }

    pCurPrc->m_AS.Activate();

    return ec;
}

ECode DebuggerStub::GetThreadStackBase(
        /* [in] */ UInt32 dwThreadId,
        /* [out] */ PVoid * pStackBase)
{
    CProcess * pProcess = ::GetCurrentProcess();

    assert(NULL != pProcess);

    InProcessLinkNode * pNode;
    Thread * pThread;
    ThreadId threadId;
    ECode ec = E_INVALID_ARGUMENT;

    pProcess->m_threadLock.Lock();

    ForEachDLinkNode(InProcessLinkNode *, pNode, &(pProcess->m_threadList)) {
        pThread = THIS(pNode, Thread, m_inProcLink);
        pThread->GetId(&threadId);
        if (threadId == (ThreadId)dwThreadId) {
            *pStackBase = (PVoid)(pThread->m_uUserStackBase + USER_STACK_SIZE);
            ec = NOERROR;
            break;
        }
    }

    pProcess->m_threadLock.Unlock();

    return ec;
}

#ifdef _arm
ECode DebuggerStub::SuspendProcess(
        /* [in] */ ProcessId dwProcessId,
        /* [in] */ Address entry,
        /* [in] */ Address nUserEntry,
        /* [in] */ PVoid nUserArg,
        /* [out] */ ThreadId *pdwThreadId)
{
    uint_t uStackTop;
    Thread *pThread;
    ECode ec;

    if (NULL == entry 
            || NULL == nUserEntry 
            || NULL == nUserArg 
            || NULL == pdwThreadId) {
        return E_INVALID_ARGUMENT;
    }

    CProcess *pDebuggee, *pDebugger;
    ForEachDLinkNode(CProcess *, pDebuggee, &(CProcess::s_processList)) {
        if (GetProcessId(pDebuggee) == dwProcessId)
            break;
    }

    assert(pDebuggee);
    if (NULL == pDebuggee) { //process with dwProcessId cannot be found
        return E_INVALID_ARGUMENT;
    }

    pDebugger = ::GetCurrentProcess();

    ec = pDebuggee->GetThread(&pThread);
    if (FAILED(ec)) {
        kprintf("pDebuggee->GetThread failed! ec = %x\n", ec);
        return ec;
    }

    assert(pThread->m_uUserStackBase);
    uStackTop = pThread->m_uUserStackBase + USER_STACK_SIZE
            - USER_STACK_PRESERVED_SIZE;

    char buf[TLS_SIZE];
    size_t NumberOfBytes = 0;
    memset((void *)buf, 0, TLS_SIZE);

    ec = ReadProcessMemory((PVoid)(((PInterface *)(pDebuggee->m_pMainThread->m_uUserStackBase 
            + USER_STACK_SIZE)) + (TL_CALLBACK_SLOT - TLS_TOTAL_SLOTS)), 
            (PVoid)((PInterface *)buf + TL_CALLBACK_SLOT),
            sizeof(UInt32), &NumberOfBytes);
    if (FAILED(ec)) {
        kprintf("read TL_CALLBACK_SLOT failed!\n");
        return ec;
    }

    // Sub-Thread derives from TL_APPLET_SLOT of Parent Thread
    // and call AddRef() when Sub-Therad is running.
    ec = ReadProcessMemory((PVoid)(((PInterface *)(pDebuggee->m_pMainThread->m_uUserStackBase 
            + USER_STACK_SIZE)) + (TL_APPLET_SLOT - TLS_TOTAL_SLOTS)), 
            (PVoid)((PInterface *)buf + TL_APPLET_SLOT),
            sizeof(UInt32), &NumberOfBytes);
    if (FAILED(ec)) {
        kprintf("read TL_CALLBACK_SLOT failed!\n");
        return ec;
    }

    // TODO: this is a temporarily implementation for applet
    ec = ReadProcessMemory((PVoid)(((PInterface *)(pDebuggee->m_pMainThread->m_uUserStackBase 
            + USER_STACK_SIZE)) + (TL_DOMAIN_SLOT - TLS_TOTAL_SLOTS)), 
            (PVoid)((PInterface *)buf + TL_DOMAIN_SLOT),
            sizeof(UInt32), &NumberOfBytes);
    if (FAILED(ec)) {
        kprintf("read TL_CALLBACK_SLOT failed!\n");
        return ec;
    }

    // Sub-Thread derives from TL_APPLET_DIR_SLOT of Parent Thread
    ec = ReadProcessMemory((PVoid)(((WChar **)(pDebuggee->m_pMainThread->m_uUserStackBase 
            + USER_STACK_SIZE)) + (TL_APPLET_DIR_SLOT - TLS_TOTAL_SLOTS)), 
            (PVoid)((WChar **)buf + TL_APPLET_DIR_SLOT),
            sizeof(UInt32), &NumberOfBytes);
    if (FAILED(ec)) {
        kprintf("read TL_CALLBACK_SLOT failed!\n");
        return ec;
    }

    ec = WriteProcessMemory(PVoid(uStackTop + USER_STACK_PRESERVED_SIZE - TLS_SIZE), 
            buf, TLS_SIZE, &NumberOfBytes);
    if (FAILED(ec)) {
        kprintf("write TLS failed!\n");
        return ec;
    }

    uStackTop = uStackTop - 12;

    //thread user entry: DebugBreakThread()
    ec = WriteProcessMemory((PVoid)(uStackTop + 4), (PVoid)&nUserEntry, 
            sizeof(PVoid), &NumberOfBytes);
    if (FAILED(ec)) {
        kprintf("write nUserEntry failed!\n");
        return ec;
    }

    //for nUserArg. actually it is useless.
    ec = WriteProcessMemory((PVoid)(uStackTop + 8), nUserArg, 
            sizeof(PVoid), &NumberOfBytes);
    if (FAILED(ec)) {
        kprintf("write nUserArg failed!\n");
        return ec;
    }

    pThread->SetUserEntry((void *)uStackTop, (void *)entry);

/*
 * for gingko mobile, it is useless to send this event to debugger,
 * since the debugger now is calling debugapi->dbgstub->here to
 * create the DebugBreakThread, and it is not waiting for the
 * event! so even this event was sent, the debugger still cannot
 * get it. at this point, the debugger should add the new thread by
 * itself, according to the returned ThreadId.
 */
    UInt32 handle[] = {(UInt32)pThread, (UInt32)nUserEntry};
    if (!TryToSendReceiveDebugMsg(DEBUGGEE_THREAD_CREATE, (UInt32)handle, pDebuggee, pThread))
    {
        kprintf("Try to send DEBUGGEE_THREAD_CREATE msg failed!\n");
    }

    ec = pThread->Start();
    if (FAILED(ec)) {
        return ec;
    }

    pThread->GetId(pdwThreadId);

    //decrease reference count
    pThread->Release();

    return NOERROR;
}
#else // _arm
ECode DebuggerStub::SuspendProcess(
        /* [in] */ ProcessId dwProcessId,
        /* [in] */ Address entry,
        /* [in] */ Address nUserEntry,
        /* [in] */ PVoid nUserArg,
        /* [out] */ ThreadId *pdwThreadId)
{
    return E_NOT_IMPLEMENTED;
}
#endif

EXTERN IDeviceDriver * CDECL CreateDebuggerStub(
    uint_t uDeviceNo, void *pvParameter)
{
    DebuggerStub * pDbg = new DebuggerStub;

    if (pDbg == NULL) {
        kprintf("Create debugger failed\n");
    }
    else {
        kprintf("Create debugger succeeded\n");
    }

    return pDbg;
}

Boolean TryToSendReceiveDebugMsg(
        /* [in] */ UInt32 reason,
        /* [in] */ UInt32 handle,
        /* [in] */ CProcess * targetProcess,
        /* [in] */ Thread * targetThread)
{
    DebuggerStub * pDbgStub = NULL;
    Boolean bPreemption;

    Thread * pTargetThread = ::GetCurrentThread();
    CProcess *pTargetProcess = pTargetThread->m_pOwner;

    if (NULL != targetThread) {
        pTargetThread = (Thread *)targetThread;
    }

    if (NULL != targetProcess) {
        pTargetProcess = (CProcess *)targetProcess;
    }

    if (NULL == pTargetProcess) {
        return FALSE;
    }

    bPreemption = DzDisablePreemption();

    pDbgStub = (DebuggerStub *)pTargetProcess->m_DebugHandle;
    if ((UInt32)pDbgStub == PROCESS_SIGNATURE) {
        DzRestorePreemption(bPreemption);
        return FALSE;
    }


    assert(pDbgStub->m_pTargetProcess == pTargetProcess);

    if (DEBUGGEE_PROCESS_STOP == reason) {
        if (pDbgStub->m_bHaveStopped) {
            DzRestorePreemption(bPreemption);
            return FALSE;
        }
        pDbgStub->m_bHaveStopped = TRUE;
    }

    pDbgStub->AddRef();

    DzRestorePreemption(bPreemption);

    pDbgStub->SendDebugEvent(pTargetProcess, pTargetThread, reason, handle);
    pDbgStub->Release();

    return TRUE;
}

#else //defined(_arm) || define(_86)

EXTERN IDeviceDriver * CDECL CreateDebuggerStub(
    uint_t uDeviceNo, void *pvParameter)
{
    return NULL;
}

Boolean TryToSendReceiveDebugMsg(
        /* [in] */ UInt32 reason,
        /* [in] */ UInt32 handle,
        /* [in] */ CProcess * targetProcess,
        /* [in] */ Thread * targetThread)
{
    return FALSE;
}

#endif //defined(_arm) || define(_86)
